/**
 * Software License, Version 1.0
 * 
 * Copyright 2003 The Trustees of Indiana University.  All rights reserved.
 * 
 *
 *Redistribution and use in source and binary forms, with or without 
 *modification, are permitted provided that the following conditions are met:
 *
 *1) All redistributions of source code must retain the above copyright notice,
 * the list of authors in the original source code, this list of conditions and
 * the disclaimer listed in this license;
 *2) All redistributions in binary form must reproduce the above copyright 
 * notice, this list of conditions and the disclaimer listed in this license in
 * the documentation and/or other materials provided with the distribution;
 *3) Any documentation included with all redistributions must include the 
 * following acknowledgement:
 *
 *"This product includes software developed by the Community Grids Lab. For 
 * further information contact the Community Grids Lab at 
 * http://communitygrids.iu.edu/."
 *
 * Alternatively, this acknowledgement may appear in the software itself, and 
 * wherever such third-party acknowledgments normally appear.
 * 
 *4) The name Indiana University or Community Grids Lab or NaradaBrokering, 
 * shall not be used to endorse or promote products derived from this software 
 * without prior written permission from Indiana University.  For written 
 * permission, please contact the Advanced Research and Technology Institute 
 * ("ARTI") at 351 West 10th Street, Indianapolis, Indiana 46202.
 *5) Products derived from this software may not be called NaradaBrokering, 
 * nor may Indiana University or Community Grids Lab or NaradaBrokering appear
 * in their name, without prior written permission of ARTI.
 * 
 *
 * Indiana University provides no reassurances that the source code provided 
 * does not infringe the patent or any other intellectual property rights of 
 * any other entity.  Indiana University disclaims any liability to any 
 * recipient for claims brought by any other entity based on infringement of 
 * intellectual property rights or otherwise.  
 *
 *LICENSEE UNDERSTANDS THAT SOFTWARE IS PROVIDED "AS IS" FOR WHICH NO 
 *WARRANTIES AS TO CAPABILITIES OR ACCURACY ARE MADE. INDIANA UNIVERSITY GIVES
 *NO WARRANTIES AND MAKES NO REPRESENTATION THAT SOFTWARE IS FREE OF 
 *INFRINGEMENT OF THIRD PARTY PATENT, COPYRIGHT, OR OTHER PROPRIETARY RIGHTS. 
 *INDIANA UNIVERSITY MAKES NO WARRANTIES THAT SOFTWARE IS FREE FROM "BUGS", 
 *"VIRUSES", "TROJAN HORSES", "TRAP DOORS", "WORMS", OR OTHER HARMFUL CODE.  
 *LICENSEE ASSUMES THE ENTIRE RISK AS TO THE PERFORMANCE OF SOFTWARE AND/OR 
 *ASSOCIATED MATERIALS, AND TO THE PERFORMANCE AND VALIDITY OF INFORMATION 
 *GENERATED USING SOFTWARE.
 */
package cgl.narada.transport.sslHttpBase;

import java.io.IOException;

import cgl.narada.transport.util.BytesUtil;

/**
 * Header fields for a message on a channel.  Provides serialization utilities
 *
 * @author  John Yin
 * @version 1.0
 */

public class LinkMessage {

    public final static byte TRANSPORT_DATA_TYPE  = 0;

    // Set the maximum data size beyond which it is considered a bad message.
    private static final int MAX_DATACHUNK_SIZE   = 10000000;

    // Message types of 10 or below are reserved.
    private byte   m_messageType = TRANSPORT_DATA_TYPE;
    private String m_channelID = "";
    private byte[] m_payload = null;

    /**
     * -------------------------------------------------------------------
     */
    public LinkMessage() {
    }

    /**
     * -------------------------------------------------------------------
     */
    public LinkMessage(String channelID) {
        m_channelID = channelID;

    }

    /**
     * -------------------------------------------------------------------
     * Create a new channel message.  Typically used for writing.
     */
    public LinkMessage(String channelID,byte[] payload) {
        m_channelID = channelID;
        m_payload   = payload;
    }

    /**
     * -------------------------------------------------------------------
     * Sets the message type flag.  Values of 10 or below are reserved.
     */
    public void setMessageType(byte type){
        m_messageType = type;
    }

    /**
     * -------------------------------------------------------------------
     * One of TRANSPOT_DATA_TYPE, or user defined type.
     */
    public byte getMessageType() {
        return m_messageType;
    }

    /**
     * -------------------------------------------------------------------
     */
    public void setPayload(byte[] payload) {
        m_payload = payload;
    }

    /**
     * -------------------------------------------------------------------
     */
    public String getLinkID() {
        return m_channelID;
    }

    /**
     * -------------------------------------------------------------------
     */
    public byte[] getPayload() {
        return m_payload;
    }

    /**
     * -------------------------------------------------------------------
     * Convert the message to a byte array for transfer.  1st byte is
     * always the message type.
     */
    public byte[] toBytes() {
        int pos = 1;
        byte[] message = new byte[9+m_channelID.length()+m_payload.length];

        // 1st byte is the message type.
        message[0] = m_messageType;

        pos += exportString(message,pos,m_channelID);
        /*System.arraycopy(BytesUtil.intToBytes(m_channelID.length()),0,message,pos,4);
        pos += 4;

        byte[] bytes = m_channelID.getBytes();
        System.arraycopy(bytes,0,message,pos,bytes.length);
        pos += bytes.length;*/

        System.arraycopy(BytesUtil.intToBytes(m_payload.length),0,message,pos,4);
        pos += 4;

        System.arraycopy(m_payload,0,message,pos,m_payload.length);
        return message;
    }

    /**
     * -------------------------------------------------------------------
     */
    public void parseData(byte[] data) throws IOException {
        m_messageType = data[0];

        // Read channel id length
        int pos = 1;

        m_channelID = importString(data,pos);
        pos += 4 + m_channelID.length();
        /* byte[] dataSizeBytes = new byte[4];
        System.arraycopy(data,pos,dataSizeBytes,0,4);
        int idSize = BytesUtil.bytesToInt(dataSizeBytes);
        pos += 4;

        // Read channel id.
        m_channelID = new String(data,pos,idSize);
        pos += idSize;*/

        // Read payload size.
        byte[] payloadSizeBytes = new byte[4];
        System.arraycopy(data,pos,payloadSizeBytes,0,4);
        int payloadSize = BytesUtil.bytesToInt(payloadSizeBytes);
        if (payloadSize > MAX_DATACHUNK_SIZE) throw new IOException("Bad size or too large: "+payloadSize);
        pos += 4;

        // Read payload
        m_payload   = new byte[payloadSize];
        System.arraycopy(data,pos,m_payload,0,payloadSize);
    }

    /**
     * -------------------------------------------------------------------
     * @return The length of the exports in bytes.
     */
    public static int exportString(byte[] buffer,int startIndex,String string) {
        System.arraycopy(BytesUtil.intToBytes(string.length()),
                         0,buffer,startIndex,4);

        System.arraycopy(string.getBytes(),0,buffer,startIndex+4,string.length());
        return 4+string.length();
    }

    /**
     * -------------------------------------------------------------------
     */
    public static String importString(byte[] buffer,int startIndex) throws IOException {
        byte[] sizeBytes = new byte[4];
        System.arraycopy(buffer,startIndex,sizeBytes,0,4);

        int size = BytesUtil.bytesToInt(sizeBytes);

        if (size > MAX_DATACHUNK_SIZE) throw new IOException("Invalid size or size too large: "+size);

        if (size == 0) return "";
        return new String(buffer,startIndex+4,size);
    }

    /**
     * -------------------------------------------------------------------
     * Unit Test
     */
    public static void main(String[] args) throws IOException {
        byte[] payload = "hello this is the united state".getBytes();

        LinkMessage msrc = new LinkMessage("helloworld",payload);
        msrc.setMessageType(LinkMessage.TRANSPORT_DATA_TYPE);
        LinkMessage mdst = new LinkMessage();
        mdst.parseData(msrc.toBytes());

        if (!mdst.getLinkID().equals("helloworld")) {
            System.out.println("===Invalid channel id");
        }

        byte[] p = mdst.getPayload();
        for(int i=0;i < payload.length;i++) {
            if (payload[i] != p[i]) {
                System.out.println("===Error in payload at position: "+i);
            }
        }

        if ((msrc.getMessageType() != LinkMessage.TRANSPORT_DATA_TYPE) &&
            (mdst.getMessageType() != LinkMessage.TRANSPORT_DATA_TYPE)) {
            System.out.println("===Error in message type");
        }

        System.out.println("Completed...");
    }
}
